/*
 * MouseEventManager.cpp
 *
 *  Created on: May 1, 2009
 *      Author: wrice
 */
#include "PANEApp.h"
namespace PANEApp {

MouseEventManager::MouseEventManager()
: lastEvent_(NULL)
{
	buildEventNameMap();
}

MyVideoTraceApp * MouseEventManager::getApp() {
	return app_;
}
void MouseEventManager::setApp( MyVideoTraceApp * app ) {
	app_ = app;
	lastEvent_ = NULL;
}

void MouseEventManager::updateView() {
	if( getApp() ) getApp()->updateView();
}

void MouseEventManager::doDefault() {
	updateView();
}

void MouseEventManager::buildEventNameMap() {
	eventMap_.insert( EventNameMapPair( CV_EVENT_MOUSEMOVE, MouseEventImp::EVENT_MOUSE_MOVE ) );
	eventMap_.insert( EventNameMapPair( CV_EVENT_LBUTTONDOWN, MouseEventImp::EVENT_MOUSE_DOWN ) );
	eventMap_.insert( EventNameMapPair( CV_EVENT_RBUTTONDOWN, MouseEventImp::EVENT_MOUSE_DOWN ) );
	eventMap_.insert( EventNameMapPair( CV_EVENT_MBUTTONDOWN, MouseEventImp::EVENT_MOUSE_DOWN ) );
	eventMap_.insert( EventNameMapPair( CV_EVENT_LBUTTONUP, MouseEventImp::EVENT_MOUSE_UP ) );
	eventMap_.insert( EventNameMapPair( CV_EVENT_RBUTTONUP, MouseEventImp::EVENT_MOUSE_UP ) );
	eventMap_.insert( EventNameMapPair( CV_EVENT_MBUTTONUP, MouseEventImp::EVENT_MOUSE_UP ) );
	eventMap_.insert( EventNameMapPair( CV_EVENT_LBUTTONDBLCLK, MouseEventImp::EVENT_DBL_CLICK ) );
	eventMap_.insert( EventNameMapPair( CV_EVENT_RBUTTONDBLCLK, MouseEventImp::EVENT_DBL_CLICK ) );
	eventMap_.insert( EventNameMapPair( CV_EVENT_MBUTTONDBLCLK, MouseEventImp::EVENT_DBL_CLICK ) );
}

void MouseEventManager::on_mouse(int event, int x, int y, int flags, void* parm) {
	MouseEventManagerSingleton::Instance().MouseEventHandler( event, x, y, flags );
}

void MouseEventManager::MouseEventHandler( int event, int x, int y, int flags ) {
    Node * const targetNode = findEventTarget( getApp()->getDocument(), x, y );
    EventTarget * const evtTarget = dynamic_cast< EventTarget * >( targetNode );
    DocumentEvent * const docEvent = dynamic_cast< DocumentEvent * >( targetNode );
	if( NULL == evtTarget || NULL == docEvent ) return;

	MouseEvents evts = createMouseEvents( docEvent, event, x, y, flags );
	if( evts.empty() ) return;

	for( MouseEventsIterator iter = evts.begin(); iter != evts.end(); ++iter ) {
		const bool bPreventDefault = dispatchEvent( *iter );
		if( false == bPreventDefault ) doDefault();
		lastEvent_ = *iter;
	}
}

Node * MouseEventManager::findEventTarget( Document * doc, int x, int y ) {
	Node * target = doc->getFirstChild();
	for( unsigned long idx = 0; idx < target->getChildNodes()->getLength(); ) {
		Node * const childNode = target->getChildNodes()->item( idx );
		VisualSegmentForPANE * const child = dynamic_cast< VisualSegmentForPANE * >( childNode );

		if( child->getLeftOffset() <= x && x < child->getRightOffset()
				&& child->getTopOffset() <= y && y < child->getBottomOffset() )
		{
			target = childNode;
			idx = 0;
			continue;
		}

		++idx;
	}
	return target;
}

MouseEvent * MouseEventManager::createMouseEvent( DocumentEvent * target, string eventType, int x, int y, int flags, EventTarget * relatedTarget ) {
	MouseEventImp * const evt = dynamic_cast< MouseEventImp * >( target->createEvent( eventType ) );
	evt->initMouseEvent(
			evt->getType(), evt->isBubbles(), evt->isCancelable(),
			NULL, 1,
			x, y, x, y,
			flags & CV_EVENT_FLAG_CTRLKEY,
			flags & CV_EVENT_FLAG_ALTKEY,
			flags & CV_EVENT_FLAG_SHIFTKEY,
			false,
			( (flags & CV_EVENT_FLAG_LBUTTON) ? MouseEvent::LEFT_MOUSE_BUTTON : 0 ) |
			( (flags & CV_EVENT_FLAG_MBUTTON) ? MouseEvent::MIDDLE_MOUSE_BUTTON : 0 ) |
			( (flags & CV_EVENT_FLAG_RBUTTON) ? MouseEvent::RIGHT_MOUSE_BUTTON : 0 ),
			relatedTarget );
	return evt;
}

MouseEventManager::MouseEvents MouseEventManager::createMouseEvents(
		DocumentEvent * const target, int event, int x, int y, int flags )
{
	MouseEvents evts;
	EventNameMapIterator iterEventMap = eventMap_.find(event);
	if( iterEventMap == eventMap_.end() ) return evts;
	const string evtName = iterEventMap->second;

	evts.push_back( createMouseEvent( target, evtName, x, y, flags, NULL ) );

	if( NULL != lastEvent_ ) {
		const string lastEvtName = lastEvent_->getType();
		DocumentEvent * const lastTarget = dynamic_cast< DocumentEvent * >( lastEvent_->getTarget() );

		if( evtName == MouseEventImp::EVENT_MOUSE_DOWN && evtName == lastEvtName )
			return evts;

		if( evtName == MouseEventImp::EVENT_MOUSE_MOVE && lastTarget != target ) {
			evts.push_back( createMouseEvent( lastTarget, MouseEventImp::EVENT_MOUSE_OUT, x, y, flags,
					dynamic_cast< EventTarget * >( target ) ) );
			evts.push_back( createMouseEvent( target, MouseEventImp::EVENT_MOUSE_OVER, x, y, flags,
					dynamic_cast< EventTarget * >( lastTarget ) ) );
		}

		if( evtName == MouseEventImp::EVENT_MOUSE_UP && lastConsistentClickTarget_ == target )
			evts.push_back( createMouseEvent( lastTarget, MouseEventImp::EVENT_CLICK, x, y, flags, NULL ) );
		if( evtName == MouseEventImp::EVENT_MOUSE_DOWN )
			lastConsistentClickTarget_ = target;
		if( lastConsistentClickTarget_ != target )
			lastConsistentClickTarget_ = NULL;
	}

	return evts;
}

bool MouseEventManager::dispatchEvent( Event * const evt ) {
	EventTarget * const target = evt->getTarget();

	typedef vector< EventTarget * > Targets;
	typedef Targets::const_iterator TargetsIterator;
	typedef Targets::const_reverse_iterator TargetsRIterator;
	Targets targets;

	for( Node * iter = dynamic_cast< Node * >( target );
		NULL != iter;
		iter = iter->getParentNode()
	) {
		targets.push_back( dynamic_cast< EventTarget *>( iter ) );
	}


	EventImp * const evtImp = dynamic_cast< EventImp * >( evt );

	evtImp->setEventPhase( Event::CAPTURING_PHASE );
	for( TargetsRIterator iterCaptuer = targets.rbegin(); iterCaptuer != targets.rend(); ++iterCaptuer )
		if( *iterCaptuer != target && false == evtImp->isStopPropagation() )
			(*iterCaptuer)->dispatchEvent( evt );

	evtImp->setEventPhase( Event::AT_TARGET );
	if( false == evtImp->isStopPropagation() )
		target->dispatchEvent( evt );

	evtImp->setEventPhase( Event::BUBBLING_PHASE );
	for( TargetsIterator iterBubble = targets.begin(); iterBubble != targets.end(); ++iterBubble)
		if( *iterBubble != target && false == evtImp->isStopPropagation() )
			(*iterBubble)->dispatchEvent( evt );

	return evtImp->isPreventDefault();
}

}
